﻿using System;
using System.Collections.Generic;
using System.Text;

namespace StarBotDotNet.TerranExample1
{
    //public class BuildingParty : UnitParty
    public class BuildingParty : UnitParty, IBuildingParty //: BuildingPartyAbstract
    {
        public class BuildOrder : Dictionary<UnitType, int>
        {
            internal void Increment(UnitType unitType)
            {
                if (!this.ContainsKey(unitType))
                {
                    this.Add(unitType, 0);
                }
                this[unitType]++;
            }

            public int GetUnitCount(UnitType unitType)
            {
                if (ContainsKey(unitType))
                {
                    return this[unitType];
                }

                return 0;
            }
        }

        BuildOrder buildOrder = new BuildOrder();


        BuildOrder producedUnits = new BuildOrder();


        public IEnumerable<KeyValuePair<UnitType, int>> EnumerateRequired(BuildOrder buildOrder, BuildOrder produced)
        {
            foreach (KeyValuePair<UnitType, int> unit in buildOrder)
            {
                int producedCount = producedUnits.GetUnitCount(unit.Key);
                int requiredCount = buildOrder.GetUnitCount(unit.Key);
                int sheduledCount = CountSheduledUnits(unit.Key);


                int total = requiredCount - (sheduledCount + producedCount);

                if (total > 0)
                {
                    yield return new KeyValuePair<UnitType, int>(unit.Key, total);
                }

            }
        }

        private int CountSheduledUnits(UnitType unitType)
        {
            int count = 0;
            foreach (BuildTarget target in targets)
            {
                if (target.UnitToBuild == unitType)
                {
                    count++;
                }
            }
            return count;
        }

        public BuildingParty(ProxyBot bot, int priority)
            : this(bot)
        {
            this.priority = priority;
        }



        public BuildingParty(ProxyBot bot)
        {
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_SCV], 10);
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_Supply_Depot], 2);
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_Barracks], 2);
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_Marine], 3);
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_Engineering_Bay], 1);
            buildOrder.Add(bot.UnitTypes[Constants.UnitTypes.Terran_Academy], 1);


        }

        internal override void PopulateWithUnits(ProxyBot bot, UnitDictionary currentUnits, List<UnitData> discoveredUnits, List<UnitData> dissapearedUnits, int currentTime)
        {
            //Handle destroyed units
            foreach (UnitData previousDiscovered in new List<UnitData>(discoveredUnhandled))
            {
                if (previousDiscovered.OwnedByPlayer)
                {
                    if (dissapearedUnits.Contains(previousDiscovered))
                    {
                        discoveredUnhandled.Remove(previousDiscovered);
                        RemoveFromTargets(previousDiscovered);
                    }
                }
            }
            int currentMinerals, currentGas;
            currentMinerals = bot.Player.UnalocatedMinerals;
            currentGas = bot.Player.UnalocatedGas;


            PutBuildOrderIntoTargets(bot, currentTime);

            this.requiredUnits = GenerateRequiredUnits(ref currentMinerals, ref currentGas);

            PopulateProbableBuildResults(discoveredUnits);




            foreach (BuildTarget target in targets)
            {
                foreach (StateCheckerAbstract checker in StateCheckerAbstract.EnumerateCheckers(target.CurrentState, target.NextState))
                {
                    checker.TryToMoveToNextState(this, target, bot, currentUnits, currentTime);
                }
            }

            RemoveFinishedTargets();

            base.PopulateWithUnits(bot, currentUnits, discoveredUnits, dissapearedUnits, currentTime);
        }







        private Dictionary<UnitType, int> GenerateRequiredUnits(ref int currentMinerals, ref int currentGas)
        {
            Dictionary<UnitType, int> requiredUnits = new Dictionary<UnitType, int>();
            foreach (BuildTarget targer in targets)
            {
                if (targer.RequiresBuilder())
                {
                    if (targer.ResourcesAllocated)
                    {
                        AddUnit(requiredUnits, targer);
                    }
                    else
                    {
                        if (targer.UnitToBuild.MineralsCost <= currentMinerals)
                        {
                            if (targer.UnitToBuild.GasCost <= currentGas)
                            {
                                currentMinerals -= targer.UnitToBuild.MineralsCost;
                                currentGas -= targer.UnitToBuild.GasCost;
                                AddUnit(requiredUnits, targer);
                            }
                        }
                    }
                }
            }
            return requiredUnits;
        }

        private static void AddUnit(Dictionary<UnitType, int> requiredUnits, BuildTarget targer)
        {
            if (!requiredUnits.ContainsKey(targer.BuilderType))
            {
                requiredUnits.Add(targer.BuilderType, 0);
            }
            requiredUnits[targer.BuilderType]++;
        }

        private void RemoveFinishedTargets()
        {
            foreach (BuildTarget target in new List<BuildTarget>(targets))
            {
                if (target.CurrentState == BuildStateEnum.b8_Finished)
                {
                    targets.Remove(target);
                    discoveredUnhandled.Remove(target.UnitProduced);
                    producedUnits.Increment(target.UnitProduced.Type);
                }
            }
        }

        private void PutBuildOrderIntoTargets(ProxyBot bot, int time)
        {
            foreach (KeyValuePair<UnitType, int> type in EnumerateRequired(buildOrder, producedUnits))
            {
                for (int i = 0; i < type.Value; i++)
                {
                    targets.Add(new BuildTarget(type.Key, time, type.Key.WhatBuildsTypeFull(bot.UnitTypes)));
                }
            }
        }

        private void PopulateProbableBuildResults(List<UnitData> discoveredUnits)
        {
            discoveredUnits = new List<UnitData>(discoveredUnits);
            discoveredUnits.AddRange(discoveredUnhandled);
            discoveredUnhandled = new List<UnitData>();
            foreach (UnitData discovered in discoveredUnits)
            {
                BuildTarget foundTarget = null;
                if (discovered.OwnedByPlayer)
                {
                    double minDistance = double.MaxValue;
                    foreach (BuildTarget target in targets.GetByState(BuildStateEnum.b5_UnderProduction))
                    {
                        if (target.UnitToBuild != discovered.Unit.Type)
                        {
                            continue;
                        }
                        if (target.UnitProduced != null)
                        {
                            continue;
                        }

                        if (IsUsedInTarget(discovered))
                        {
                            continue;
                        }
                        double currDistance = target.CalculateDistance(discovered);
                        if (currDistance < minDistance)
                        {
                            foundTarget = target;
                            minDistance = currDistance;
                        }

                    }

                    if (foundTarget == null)
                    {
                        foreach (BuildTarget target in targets.GetByState(BuildStateEnum.b0_Initial))
                        {
                            if (target.NextState != BuildStateEnum.b0_Initial)
                            {
                                continue;
                            }
                            if (target.UnitProduced != null)
                            {
                                continue;
                            }

                            if (IsUsedInTarget(discovered))
                            {
                                continue;
                            }
                            if (target.UnitToBuild == discovered.Unit.Type)
                            {
                                foundTarget = target;
                                break;
                            }
                        }
                    }

                    if (foundTarget != null)
                    {
                        foundTarget.UnitProduced = discovered;
                    }
                    else
                    {
                        discoveredUnhandled.Add(discovered);
                    }

                }


            }
        }

        private bool IsUsedInTarget(UnitData discovered)
        {
            foreach (BuildTarget target in targets)
            {
                if (target.UnitProduced == discovered)
                {
                    return true;
                }
            }
            return false;
        }

        public override void ExecuteTactics(StarBotDotNet.ProxyBot bot, UnitDictionary currentUnits, int currentTime)
        {
            //int i = 0;
            //while (i < targets.Count)
            //{
            //    BuildTarget target = targets[i];

            //    if (target.HasFinishedWithSuccess)
            //    {
            //        targets.Remove(target);
            //        producedUnits.Increment(target.UnitToBuild);
            //        continue;
            //    }

            //    if (!target.HasBuilder())
            //    {
            //        target.Builder = FindUnusedBuilder(target.BuilderType);
            //    }

            //    if (target.Builder != null)
            //    {
            //        if (target.IsWorking())
            //        {

            //            if (target.IsOverdue(currentTime))
            //            {

            //                System.Console.WriteLine("Overdue by: {0}", target.CalculateOverdue(currentTime));
            //                //TODO:Deal with overdue units
            //            }




            //        }
            //        else
            //        {
            //            if (!LocationValid(target.BuildLocation.X, target.BuildLocation.Y, target.UnitToBuild.TileWidth, target.UnitToBuild.TileHeight, bot.Map, currentUnits, target.Builder))
            //            {
            //               FindLocationForUnit(target.UnitToBuild, bot, currentUnits);
            //            }


            //            if (target.LocationReached())
            //            {
            //                target.InitiateBuilding(bot, currentTime);
            //            }
            //            else
            //            {
            //                if (target.Builder.IsIdle)
            //                {
            //                    bot.rightClick(target.Builder.Unit.ID, target.BuildLocation.X, target.BuildLocation.Y);
            //                }
            //            }


            //        }
            //    }

            //    i++;
            //}


        }



        //public override  UnitData FindUnusedBuilder(UnitType unitType)
        //{
        //    foreach (UnitData item in GetAllUnits())
        //    {
        //        if (item.Type == unitType)
        //        {

        //            if (IsUnusedBuilder(item))
        //            {
        //                return item;
        //            }
        //        }
        //    }
        //    return null;
        //}

        private bool IsUnusedBuilder(UnitData item)
        {
            if (!item.IsIdle)
            {
                return false;
            }

            if (item.IsDead)
            {
                return false;
            }

            if (!item.Unit.IsCompleted)
            {
                return false;
            }

            foreach (BuildTarget target in targets)
            {
                if (target.Builder == item)
                {
                    return false;
                }
            }
            return true;
        }

        private int priority = int.Parse(System.Configuration.ConfigurationSettings.AppSettings["BuildingBotsPriority"]);
        public override int Priority
        {
            get { return priority; }

        }


        /// <summary>
        /// To be able to handle discovered units on later frames if HasFinished has not yet elapsed
        /// </summary>
        List<UnitData> discoveredUnhandled = new List<UnitData>();

        public List<UnitData> DiscoveredUnhandled
        {
            get { return discoveredUnhandled; }
        }



        private void RemoveFromTargets(UnitData previousDiscovered)
        {
            foreach (BuildTarget target in targets)
            {
                if (target.Builder == previousDiscovered)
                {
                    target.Builder = null;
                }

                if (target.UnitProduced == previousDiscovered)
                {
                    target.UnitProduced = null;
                }
            }
        }



        public class BuildTargets : List<BuildTarget>
        {
            internal IEnumerable<BuildTarget> GetByState(BuildStateEnum buildStateEnum)
            {
                foreach (BuildTarget target in this)
                {
                    if (target.CurrentState == buildStateEnum)
                    {
                        yield return target;
                    }
                }
            }
        }

        BuildTargets targets = new BuildTargets();

        Dictionary<UnitType, int> requiredUnits = new Dictionary<UnitType, int>();

        //Dictionary<UnitType, Dictionary<UnitData,UnitType>> requiredUnits = new Dictionary<UnitType, int>();
        public override Dictionary<UnitType, int> RequiredUnits
        {
            get
            {

                return requiredUnits;
            }
        }


        protected override bool IsUnused(UnitData data)
        {
            foreach (BuildTarget target in targets)
            {
                if (target.UsesUnit(data))
                {
                    return false;
                }
            }

            return true;
        }


        #region IBuildingParty Members

        UnitData IBuildingParty.FindUnusedBuilder(UnitType unitType)
        {
            foreach (UnitData item in GetAllUnits())
            {
                if (item.Type == unitType)
                {

                    if (IsUnusedBuilder(item))
                    {
                        return item;
                    }
                }
            }
            return null;
        }

        #endregion


        //BuildTargets targets = new BuildTargets();
        public BuildTargets Targets
        {
            get { return targets; }
        }
        public bool IsBuildingTargets
        {
            get { return targets.Count != 0; }
        }
    }
}
